package com.cskaoyan.javase.oop3._2inner._6lambda._0introduction;

/**
 * Lambda 表达式是 JDK8 的一个新特性，可以取代接口的匿名内部类，写出更优雅的Java 代码。
 * 如果说匿名内部类实际上是局部内部类的更进一步，简化了局部内部类，
 * 那么Lambda就是匿名内部类更进一步，语法上更简洁了，代码更优雅了，是高端的玩法，是人上人的玩法。
 * 以上描述总体就说了三点：
 *      1.Lambda表达式仍然是局部内部类，是特殊的局部内部类，仍然定义在局部位置。
 *          而且局部内部类的注意事项，也一样存在。
 *          如果访问方法的局部变量,那么这个局部变量应该是一个常量
 *      2.Lambda表达式在取代匿名内部类时，不是全部都取代，
 *          而是取代接口的匿名内部类，而类的匿名内部类Lambda表达式是不能取代的。
 *      3.Lambda表达式是匿名内部类的更进一步， Lambda表达式得到的也不是一个类，而是一个对象，并且是接口的子类对象。
 *
 * Lambda表达式在取代接口的匿名内部类对象时,
 *      注意不是所有的接口都可以,而是要求接口必须是功能接口(Functional Interface)
 * 功能接口是什么?
 *      功能接口指的是,在接口中,仅有一个必须要子类实现的抽象方法的接口
 *      在语法上,我们使用注解@FunctionalInterface来标记功能接口,如果是就不会编译报错,反之会报错!
 *      辨析:
 *          1.功能接口中只有一个方法.
 *              不对,接口还有一些实现方法,这些方法不需要子类实现,存在这些方法,不影响是功能接口.
 *              指的是Java8以后的默认方法和静态方法
 *
 *          2.功能接口中只有一个抽象方法.
 *              不对,有些抽象方法不需要(普通)子类实现
 *              不要忘记Java中每一个类都有一个的父类,就是Object
 *              如果Object类当中有该抽象方法的实现,那么子类就可以把Object类的默认实现当成这个抽象方法的实现,于是这个抽象方法不需要强制实现了.
 *
 *         当然实际上,大多数功能接口还是仅有一个抽象方法,没有多余的方法
 *
 *
 * 有了功能接口后,就可以写Lambda表达式,用来创建功能接口接口的子类对象
 * 语法:
 *      () -> {}
 * 解释:
 *      1."()"是功能接口中,那个必须要子类重写的抽象方法的,形参列表
 *          说白了,功能接口中,抽象方法的形参列表长什么样,这里就是什么样
 *      2."->" 读叫"goes to",它是Lambda表达式的运算符,就是一个语法
 *          它是一个横杠加上一个大于号组合起来的
 *      3."{}"是功能接口中,那个必须要子类重写的抽象方法的,重写方法体
 *
 *      以上需要注意:
 *          1.因为Lambda就只能重写一个方法,所以Lambda表达式要求接口必须是功能接口
 *              必须有且仅有一个必须要子类实现的抽象方法
 *          2.注意语法中"{}"不再是类体了,而是重写方法的方法体.
 *              这意味着: Lambda表达式表示的子类对象不能新增自身成员,因为它做不到
 *
 * 接下来开始使用Lambda表达式创建功能接口的子类对象:
 *
 * 直接在局部位置写Lambda表达式的语法 会报错,为什么?
 * 因为直接写Lambda表达式语法,编译器无从得知,Lambda表达式所表示的子类对象的类型,不知道它是哪个功能接口的子类对象
 * 所以为了让代码不报错,就需要告诉编译器Lambda表达式所表示的子类对象的类型,这个过程称之为"Lambda表达式的类型推断"
 *
 * 怎么做Lambda表达式的类型推断呢?
 *      要想做类型推断,肯定需要根据上下文信息做一些推断,如果没有额外信息是推断不了的
 *      所以Lambda表达式的类型推断主要包括四种场景:
 *          1.直接用功能接口的引用接收
 *              简单直接,完成类型推断
 *
 *          2.不用引用接收,但是用特殊的语法告诉编译器Lambda表达式的具体类型.
 *          语法:
 *          (功能接口名)Lambda表达式
 *          语法有点类似于强转,但不是强转
 *          而且由于这个Lambda表达式没有引用接收,只是指明了类型,所以必须直接使用它
 *          ((功能接口名)Lambda表达式).方法名();
 *
 *          以上两种方式用的都不多
 *          3.借助方法的返回值类型
 *              因为方法返回什么对象是固定
 *
 *          4,借助方法的形参数据类型
 *              因为方法需要什么对象参数是固定的
 *
 *        实际开发中,方式3和4是最常见的
 *        Lambda表达式基本都是在方法中使用
 *        尤其是作为参数,是最常见的!!
 *
 * 以上，Lambda表达式最基础使用，就完成了。
 *
 *
 * @since 17:05
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 在这里创建功能接口IDemo的子类对象,用Lambda表达式
        IDemo id1 = () -> {
            System.out.println("hello world!");
        };
        id1.test();

        IA ia = () -> {
            // 方法体
            System.out.println("我是高端的玩法!");
        };
        ia.test();

        ((IDemo) () -> {
            System.out.println("666!");
        }).test();

        IDemo iDemo = method();
        iDemo.test();

        method(() -> {
            System.out.println("77777!");
        });

        // 手写IB功能接口的子类对象,用Lambda表达式
        IB ib = (int a, int b) -> {
            return (a > b) ? a : b;
        };
        System.out.println(ib.test(100, 200));

    }

    public static IDemo method() {
        /*return new IDemo() {
            @Override
            public void test() {
                System.out.println("我是匿名内部类的实现!");
            }
        };*/
        return () -> {
            System.out.println("我是Lambda表达式的是实现,我更高端!");
        };
    }

    public static void method(IDemo id) {
        id.test();
    }
}

@FunctionalInterface
interface IB {
    int test(int a, int b);
}

@FunctionalInterface
interface IDemo {
    void test();
}

class IAImpl implements IA {
    @Override
    public void test() {
        System.out.println("hello world!");
    }
}

@FunctionalInterface
interface IA {
    void test();

    default void test2() {
    }

    static void test3() {
    }

    // 对应Object类当中的hashCode方法
    int hashCode();

    // 对应Object类当中的equals方法
    boolean equals(Object o);
}

// 以下都不是功能接口
/*
@FunctionalInterface
interface IB {
}
@FunctionalInterface
interface IC {
    void test();

    void test2();
}*/
